﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.DynamicData;
using System.IO;
using System.Security;
using System.Web;
using System.Web.SessionState;

namespace NotAClue.Web
{
    public static class PageExtensionMethods
    {
        /// <summary>
        /// Gets the 'parameterName' QueryString value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="page">The page.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static T GetQueryStringValueAs<T>(this Page page, String parameterName)
        {
            // get and convert value
            var stringValue = page.Request.QueryString[parameterName];
            if (!String.IsNullOrEmpty(stringValue))
            {
                var value = (T)Convert.ChangeType(stringValue, typeof(T));
                return value;
            }
            return default(T);
        }

        /// <summary>
        /// Redirects the tot file.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="relativeFilePath">The relative file path.</param>
        /// <param name="showError">if set to <c>true</c> [show error].</param>
        /// <param name="inline">if set to <c>true</c> [inline].</param>
        public static void RedirectTotFile(this Page page, String relativeFilePath, Boolean showError = true, Boolean inline = true)
        {
            // set default status code
            var statusCode = 200;

            // get full file path
            var filePath = page.Server.MapPath(relativeFilePath);

            // is in-line (open in browser) or attachment download.
            String fileType = inline ? "inline" : "attachment";

            var file = new FileInfo(filePath);
            // check file exists
            if (!file.Exists)
                throw new FileNotFoundException(String.Format("File '{0}' does not exist", file.Name));

            try
            {
                page.Response.Clear();
                page.Response.ClearContent();
                page.Response.ClearHeaders();
                page.Response.Cookies.Clear();

                // turn caching on
                page.Response.AppendHeader("Pragma", "cache");

                // set cache expiry time
                page.Response.AppendHeader("Expires", "10");

                // declare file length
                page.Response.AppendHeader("Content-Length", file.Length.ToString());

                page.Response.Buffer = true;

                string strHeaderText = String.Format("{0}; filename=\"{1}\"; size={2}; creation-date={3:R}; modification-date={4:R}; read-date={5:R}",
                    fileType,
                    file.Name,
                    file.Length,
                    file.CreationTime,
                    file.LastAccessTime,
                    DateTime.Now);

                // use attachment for download and in-line for viewing
                page.Response.AppendHeader("Content-Disposition", strHeaderText);

                // Get correct mime type based on Extension
                var mimeType = file.GetMimeType();

                //Set the appropriate ContentType.
                page.Response.ContentType = inline ? mimeType : "application/octet-stream";

                // get file as byte stream
                FileStream myFileStream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read);
                long FileSize = myFileStream.Length;
                byte[] Buffer = new byte[(int)FileSize];
                myFileStream.Read(Buffer, 0, (int)FileSize);
                myFileStream.Close();

                //Write the file directly to the HTTP content output stream.
                page.Response.BinaryWrite(Buffer);
            }
            // Note: exception variable left for debug purposes
            // Useful status codes http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
            catch (FileNotFoundException)
            {
                // 404 Not Found - The requested resource could not be found but may be available again in the future.
                // Subsequent requests by the client are permissible
                statusCode = 404;
            }
            catch (DirectoryNotFoundException)
            {
                // 404 Not Found - The requested resource could not be found but may be available again in the future.
                // Subsequent requests by the client are permissible
                statusCode = 404;
            }
            catch (PathTooLongException)
            {
                // 404 Not Found - The requested resource could not be found but may be available again in the future.
                // Subsequent requests by the client are permissible
                statusCode = 404;
            }
            catch (IOException)
            {
                // 404 Not Found - The requested resource could not be found but may be available again in the future.
                // Subsequent requests by the client are permissible
                statusCode = 404;
            }
            catch (NotSupportedException)
            {
                // 400 Bad page.Request  - The request contains bad syntax or cannot be fulfilled.                
                statusCode = 400;
            }
            catch (UnauthorizedAccessException)
            {
                // 403 Forbidden - The request was a legal request, but the server is refusing to respond to it.
                // Unlike a 401 Unauthorized response, authenticating will make no difference.
                statusCode = 403;
            }
            catch (SecurityException)
            {
                // 403 Forbidden - The request was a legal request, but the server is refusing to respond to it.
                // Unlike a 401 Unauthorized response, authenticating will make no difference.
                statusCode = 403;
            }
            catch (HttpException)
            {
                // 400 Bad page.Request  - The request contains bad syntax or cannot be fulfilled.                
                statusCode = 400;
            }
            catch (Exception)
            {
                // 500 Internal Server Error - A generic error message, given when no more specific message is suitable
                statusCode = 500;
            }
            finally
            {
                // if errors enabled
                if (showError)
                    page.Response.StatusCode = statusCode;

                page.Response.Flush();
                page.Response.End();
            }
        }

        /// <summary>
        /// Sets the pop-up message.
        /// </summary>
        /// <param name="updatePanel">The page.</param>
        /// <param name="message">The message.</param>
        /// <remarks></remarks>
        public static void SetPopupMessage(this UpdatePanel updatePanel, String message)
        {
            var script = String.Format("alert('{0}');", message);
            ScriptManager.RegisterStartupScript(updatePanel, updatePanel.GetType(), "NAC_UpdateAlert", script, true);
        }

        /// <summary>
        /// Sets the pop-up message.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="message">The message.</param>
        /// <remarks></remarks>
        public static void SetPopupMessage(this Page page, String message)
        {
            var csm = page.ClientScript;
            var script = String.Format("window.onload = function () {{ alert('{0}');}}", message);
            csm.RegisterStartupScript(page.GetType(), "NAC_alert", script, true);
        }

        /// <summary>
        /// Disables the partial render.
        /// </summary>
        /// <param name="page">The page.</param>
        public static void DisablePartialRender(this Page page)
        {
            var sm = ScriptManager.GetCurrent(page);
            if (sm != null)
                sm.EnablePartialRendering = false;
        }

        /// <summary>
        /// Sets the favicon.
        /// </summary>
        /// <param name="page">The page.</param>
        public static void SetFavicon(this Page page)
        {
            var authority = page.Request.Url.AbsoluteUri
                .Replace(page.Request.Url.Segments[page.Request.Url.Segments.Length - 1], "")
                .Replace("http://", "")
                .Replace("https://", "");

            var href = VirtualPathUtility.AppendTrailingSlash(String.Format("{0}://{1}", page.Request.Url.Scheme, authority)) + "favicon.ico";
            String shortcutIconRel = "shortcut icon";
            String iconRel = "icon";

            //<link href="~/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"/>
            var favicon1 = new HtmlLink();
            favicon1.Href = href;
            favicon1.Attributes.Add("rel", shortcutIconRel);
            page.Header.Controls.Add(favicon1);

            //<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon"/>
            var favicon2 = new HtmlLink();
            favicon2.Href = href;
            favicon2.Attributes.Add("rel", iconRel);
            favicon2.Attributes.Add("type", "image/vnd.microsoft.icon");
            page.Header.Controls.Add(favicon2);

            //<link href="~/favicon.ico" rel="icon" type="image/x-icon">
            var favicon3 = new HtmlLink();
            favicon3.Href = href;
            favicon3.Attributes.Add("rel", iconRel);
            favicon3.Attributes.Add("type", "image/x-icon");
            page.Header.Controls.Add(favicon3);
        }

        /// <summary>
        /// Adds the client script.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="key">The key.</param>
        /// <param name="script">The script.</param>
        public static void AddClientScript(this Page page, String key, String script, Boolean addScriptTages = true)
        {
            // Define the name and type of the client scripts on the page.
            Type scriptType = page.GetType();

            // Get a ClientScriptManager reference from the Page class.
            ClientScriptManager csm = page.ClientScript;

            // Check to see if the Client Script Include is already registered.
            if (!csm.IsClientScriptBlockRegistered(scriptType, key))
            {
                // include main Flash Content script
                csm.RegisterClientScriptBlock(scriptType, key, script, addScriptTages);
            }
        }

        /// <summary>
        /// Adds the startup client script.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="key">The key.</param>
        /// <param name="script">The script.</param>
        /// <param name="addScriptTages">if set to <c>true</c> [add script tages].</param>
        /// <remarks></remarks>
        public static void AddStartupClientScript(this Page page, String key, String script, Boolean addScriptTages = true)
        {
            // Define the name and type of the client scripts on the page.
            Type scriptType = page.GetType();

            // Get a ClientScriptManager reference from the Page class.
            ClientScriptManager csm = page.ClientScript;

            // Check to see if the Client Script Include is already registered.
            if (!csm.IsStartupScriptRegistered(scriptType, key))
            {
                // include main Flash Content script
                csm.RegisterStartupScript(scriptType, key, script, addScriptTages);
            }
        }

        /// <summary>
        /// Adds the CSS to head.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="styleSheetUrl">The style sheet URL.</param>
        public static void AddCssToHead(this Page page, String styleSheetUrl)
        {
            var styleSheetName = styleSheetUrl.Substring(styleSheetUrl.LastIndexOf("/") + 1, styleSheetUrl.Length - (styleSheetUrl.LastIndexOf("/") + 1));
            if (!page.HasStyleSheet(styleSheetName))
            {
                var link = new HtmlLink() { Href = styleSheetUrl };
                link.Attributes.Add("rel", "stylesheet");
                link.Attributes.Add("type", "text/css");
                page.Header.Controls.Add(link);
            }
        }

        /// <summary>
        /// Determines whether [has style sheet] [the specified page].
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="styleSheet">The style sheet.</param>
        /// <returns>
        /// 	<c>true</c> if [has style sheet] [the specified page]; otherwise, <c>false</c>.
        /// </returns>
        public static Boolean HasStyleSheet(this Page page, String styleSheet)
        {
            var links = from l in page.Header.Controls.OfType<HtmlLink>()
                        where l.Href.Contains(styleSheet)
                        select l;

            return links.Count() > 0;
        }

        /// <summary>
        /// Add a client script or CSS file to the header once per page
        /// </summary>
        /// <param name="page">of type System.Web.UI.Page</param>
        /// <param name="scriptName">Name of the script.</param>
        /// <param name="clientScript">Client script content</param>
        /// <param name="addScriptTags">if set to <c>true</c> [add script tags].</param>
        public static void RegisterClientScript(this Page page, String scriptName, String clientScript, Boolean addScriptTags = false)
        {
            // Define the name and type of the client scripts on the page.
            Type scriptType = page.GetType();

            // Get a ClientScriptManager reference from the Page class.
            ClientScriptManager csm = page.ClientScript;

            // Check to see if the Client Script Include is already registered.
            if (!csm.IsClientScriptBlockRegistered(scriptType, clientScript))
                csm.RegisterClientScriptBlock(scriptType, scriptName, clientScript, addScriptTags);
        }

        /// <summary>
        /// Add a client script or CSS file to the header once per page
        /// </summary>
        /// <param name="page">of type System.Web.UI.Page</param>
        /// <param name="scriptFile">path to script file</param>
        public static void IncludeClientScrip(this Page page, String scriptFile)
        {
            // Define the name and type of the client scripts on the page.
            string scriptName = scriptFile.GetFileName();
            Type scriptType = page.GetType();

            // Get a ClientScriptManager reference from the Page class.
            ClientScriptManager csm = page.ClientScript;

            // Check to see if the Client Script Include is already registered.
            if (!csm.IsClientScriptIncludeRegistered(scriptType, scriptName))
            {
                // include main Flash Content script
                string urlJS = page.ResolveUrl(scriptFile);
                csm.RegisterClientScriptInclude(scriptType, scriptName, urlJS);
            }
        }

        ///// <summary>
        ///// Adds the script to head.
        ///// </summary>
        ///// <param name="page">The page.</param>
        ///// <param name="scriptName">Name of the script.</param>
        //public static void AddScriptToHead(this Page page, String scriptName)
        //{
        //    var scriptPath = String.Format("~/javascript/{0}.js", scriptName);

        //    var includePresent = page.Header.FindControl(scriptName);
        //    if (includePresent == null)
        //    {
        //        // create the style sheet control and put it in the document header
        //        var linkRef = "<script src=\"{0}\" type=\"text/javascript\"></script>";
        //        LiteralControl include = new LiteralControl(String.Format(linkRef, page.ResolveClientUrl(scriptPath)));
        //        include.ID = scriptName;
        //        page.Header.Controls.Add(include);
        //    }
        //}

        /// <summary>
        /// Add a client script or css file to the header once per page
        /// </summary>
        /// <param name="page">of type System.Web.UI.Page</param>
        /// <param name="styleSheet">path to style sheet file</param>
        /// <param name="scriptType">An enum of the file types supported</param>
        public static void AddStyleToHead(this Page page, String styleSheet)
        {
            //var controlId = page.GetType().Name + "." + scriptFile.GetFileName();
            var controlId = styleSheet.GetFileNameTitle();
            var includePresent = page.Header.FindControl(controlId);
            if (includePresent == null)
            {
                //<link href="../css/style.css" rel="stylesheet" type="text/css" />
                var link = new HtmlLink();
                link.ID = controlId;
                link.Href = page.ResolveUrl(styleSheet);
                link.Attributes.Add("rel", "stylesheet");
                link.Attributes.Add("type", "text/css");
                page.Header.Controls.Add(link);
            }
        }

        /// <summary>
        /// Gets the file extension from a string.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>file extension of the filename</returns>
        public static String GetFileExtension(this String fileName)
        {
            return fileName.Substring(fileName.LastIndexOf(".") + 1).ToLower();
        }

        /// <summary>
        /// Replaces the file extension.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="newExt">The new ext.</param>
        /// <returns></returns>
        public static String ReplaceFileExtension(this String fileName, String newExt)
        {
            var ext = fileName.Substring(fileName.LastIndexOf(".") + 1).ToLower();
            return fileName.Replace(ext, newExt);
        }

        /// <summary>
        /// Gets the name of the file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static String GetFileName(this String fileName)
        {
            String pathSeperator = fileName.GetPathSeperator();
            return fileName.Substring(fileName.LastIndexOf(pathSeperator) + 1);
        }

        /// <summary>
        /// Gets the file name title.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static String GetFileNameTitle(this String fileName)
        {
            String pathSeperator = fileName.GetPathSeperator();
            return fileName.Substring(fileName.LastIndexOf(pathSeperator) + 1,
                (fileName.LastIndexOf(".") - 1) - fileName.LastIndexOf(pathSeperator));
        }

        /// <summary>
        /// Gets the path seperator.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static String GetPathSeperator(this String fileName)
        {
            String pathSeperator;

            if (fileName.Contains("\\"))
                pathSeperator = "\\";
            else
                pathSeperator = "/";
            return pathSeperator;
        }
    }
}
